/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <boost/any.hpp>

#include "formatters/formatters.h"
#include "formatters/ml_formatters.h"
#include "pipeline/pipeline_io.h"
#include "machine_learning/statistical_models/normal_bayes_model.h"
#include "factory/ResourceFormatterFactory.h"

using namespace std;
using namespace cv;

namespace NAMESPACE{

// ****** BayesHgWndFormatter implementation ***********************************
//  _____________________________________________________
// |   Standard constructors                             |
// |_____________________________________________________|

BayesHgWndFormatter::~BayesHgWndFormatter(){
    cvDestroyAllWindows();
}
BayesHgWndFormatter::BayesHgWndFormatter() : AbstractFormatter(){}
BayesHgWndFormatter::BayesHgWndFormatter(const BayesHgWndFormatter& f) :  AbstractFormatter(f){}
BayesHgWndFormatter& BayesHgWndFormatter::operator=(const BayesHgWndFormatter& f){
    if(this == &f) return *this;
    AbstractFormatter::operator =(f);
    return *this;
}
// ......................................................

BayesHgWndFormatter::BayesHgWndFormatter(const IOKey& k) : AbstractFormatter(k){}

Formatter& BayesHgWndFormatter::format() const{ throw std::runtime_error("Not implemented"); }
Formatter& BayesHgWndFormatter::format(const IOElement& el) const{
    NormalBayesModel* model_interface = boost::any_cast<NormalBayesModel*>(el.getElement());
    Discret2DNormalBayesModel* model = dynamic_cast<Discret2DNormalBayesModel*>(model_interface);

    vector<Mat> luts = model->get_with_evidence_data_luts();
    prob_t max = 0, scale;
    MatIterator_<prob_t> it;
    Mat* sm;
    Mat m_show;
    for(unsigned int i = 0; i < luts.size(); i++){
        sm = &(luts[i]);
        // find max in current LUT
        max = *max_element(sm->begin<prob_t>(), sm->end<prob_t>());
        scale = max ? (1.0 / max) * 255 : 0;
        if(max) sm->convertTo(m_show, CV_8U, scale);

        stringstream ss;
        ss << "Distribution " << i;
        cv::imshow(ss.str(), m_show);
    }
    cv::waitKey(2);

    return *const_cast<BayesHgWndFormatter*>(this);
}
// -----------------------------------------------------------------------------

// ****** CvMatXMLFormatter implementation ***********************************
//  _____________________________________________________
// |   Standard constructors                             |
// |_____________________________________________________|

const std::string BayesXMLFormatter::DEFAULT_NAME = "unnamed.xml";

BayesXMLFormatter::~BayesXMLFormatter(){
    cvDestroyAllWindows();
}
BayesXMLFormatter::BayesXMLFormatter() : AbstractFormatter(), name(DEFAULT_NAME){}
BayesXMLFormatter::BayesXMLFormatter(const BayesXMLFormatter& f) :  AbstractFormatter(f), name(f.name){}
BayesXMLFormatter& BayesXMLFormatter::operator=(const BayesXMLFormatter& f){
    if(this == &f) return *this;
    AbstractFormatter::operator =(f);
    name = f.name;
    return *this;
}
// ......................................................

BayesXMLFormatter::BayesXMLFormatter(const IOKey& k, string _name) : AbstractFormatter(k), name(_name){}

Formatter& BayesXMLFormatter::format() const{ throw std::runtime_error("Not implemented"); }
Formatter& BayesXMLFormatter::format(const IOElement& el) const{
    NormalBayesModel* model_interface = boost::any_cast<NormalBayesModel*>(el.getElement());
    Discret2DNormalBayesModel* model = dynamic_cast<Discret2DNormalBayesModel*>(model_interface);

    vector<Mat> luts = model->get_with_evidence_data_luts();
    FileStorage fs(name, FileStorage::WRITE);
    fs << A_POSTERIORI_DATA_NODE_NAME << "[";
    for(unsigned int i = 0; i < luts.size(); i++){
       fs << luts[i];
    }
    fs << "]";
    fs << A_PRIORI_DATA_NODE_NAME  << model->get_a_priori_data_lut();
    fs << A_PRIORI_CLASS_NODE_NAME << model->get_a_priori_evidence();

    fs.release();

    return *const_cast<BayesXMLFormatter*>(this);
}
// -----------------------------------------------------------------------------

// ****** CameraInputGeneratorFactory implementation ***************************
REGISTER_PARSE_TYPE(BayesXMLFormatter);
ResourceFormatterFactory<BayesXMLFormatter> bayesXMLFormatter_instance;

// -----------------------------------------------------------------------------

// ****** BayesImgFileFormatter implementation *********************************
//  _____________________________________________________
// |   Standard constructors                             |
// |_____________________________________________________|

const std::string BayesImgFileFormatter::DEFAULT_NAME = "unnamed";

BayesImgFileFormatter::~BayesImgFileFormatter(){
    cvDestroyAllWindows();
}
BayesImgFileFormatter::BayesImgFileFormatter() : AbstractFormatter(), name(DEFAULT_NAME){}
BayesImgFileFormatter::BayesImgFileFormatter(const BayesImgFileFormatter& f) :  AbstractFormatter(f), name(f.name){}
BayesImgFileFormatter& BayesImgFileFormatter::operator=(const BayesImgFileFormatter& f){
    if(this == &f) return *this;
    AbstractFormatter::operator =(f);
    name = f.name;
    return *this;
}
// ......................................................

BayesImgFileFormatter::BayesImgFileFormatter(const IOKey& k, string _name) : AbstractFormatter(k), name(_name){}

Formatter& BayesImgFileFormatter::format() const{ throw std::runtime_error("Not implemented"); }
Formatter& BayesImgFileFormatter::format(const IOElement& el) const{
    NormalBayesModel* model_interface = boost::any_cast<NormalBayesModel*>(el.getElement());
    Discret2DNormalBayesModel* model = dynamic_cast<Discret2DNormalBayesModel*>(model_interface);

    vector<Mat> luts = model->get_with_evidence_data_luts();
    luts.push_back(model->get_a_priori_data_lut());
    prob_t max = 0;
    Mat m;
    for(unsigned int i = 0; i < luts.size(); i++){
        max = *max_element(luts[i].begin<prob_t>(), luts[i].end<prob_t>());
        luts[i].convertTo( m, CV_8U, (max ? (255.0 / max) : 0) );
        
        stringstream ss;
        ss << name << i << ".png";
        cv::imwrite( ss.str(),  m);
    }
    return *const_cast<BayesImgFileFormatter*>(this);
}
// -----------------------------------------------------------------------------

}
